package com.zn.opit.quartzdemo.service.impl;

import com.zn.opit.quartzdemo.domain.entity.Qrtz;
import com.zn.opit.quartzdemo.exception.QuartzDemoException;
import com.zn.opit.quartzdemo.exception.QuartzDemoExceptionTypes;
import com.zn.opit.quartzdemo.mapper.QrtzMapper;
import com.zn.opit.quartzdemo.service.QrtzService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service(value = "qrtzService")
public class QrtzServiceImpl implements QrtzService {

    private final String cronRegex = "^(\\d+|\\*|[a-zA-Z]+\\-\\d+|\\d+\\/\\d+|\\d+(,\\d+)*)$";

    @Resource
    private QrtzMapper qrtzMapper;
    @Resource
    private Scheduler scheduler;

    @Override
    public List<Qrtz> listAllQrtz(Qrtz qrtz) {
        return qrtzMapper.selectQrtzList(qrtz).stream().peek(item -> {
            JobKey key = new JobKey(item.getName(), item.getGroup());
            JobDetail jobDetail = null;
            try {
                jobDetail = scheduler.getJobDetail(key);
            } catch (SchedulerException e) {
                throw new QuartzDemoException(QuartzDemoExceptionTypes.JOB_GET_FAILED);
            }
            assert jobDetail != null;
            item.setMethodName(jobDetail.getJobDataMap().getString("methodName"));
        }).collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    @Override
    public Date createJob(Qrtz qrtz) {
        // 正则表达式校验
        if (isValidCron(qrtz.getCron())) {
            throw new QuartzDemoException(QuartzDemoExceptionTypes.CRON_EXP_NOT_CORRECT);
        }

        // 构建job信息
        Class<Job> cls = null;
        try {
            cls = (Class<Job>) Class.forName(qrtz.getClazz());
        } catch (ClassNotFoundException e) {
            throw new QuartzDemoException(QuartzDemoExceptionTypes.CLASS_NOT_FOUND.getCode(), QuartzDemoExceptionTypes.CLASS_NOT_FOUND.getMsg() + ":" + qrtz.getClazz());
        }
        JobDetail job = JobBuilder.newJob(cls)
                .withIdentity(qrtz.getName(), qrtz.getGroup())
                .withDescription(qrtz.getDesc()).build();

        // 根据Corn表达式构建触发时间点
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(qrtz.getCron());
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger" + qrtz.getName(), qrtz.getGroup())
                .withDescription(qrtz.getDesc())
                .startNow().withSchedule(cronScheduleBuilder).build();

        // 交由Scheduler安排触发
        job.getJobDataMap().put("methodName", qrtz.getMethodName());
        try {
            return scheduler.scheduleJob(job, trigger);
        } catch (SchedulerException e) {
            throw new QuartzDemoException(QuartzDemoExceptionTypes.JOB_CREATE_FAILED);
        }
    }

    @Override
    public void remove(String name, String group) {
        try {
            JobKey key = new JobKey(name, group);
            if (!scheduler.checkExists(key)) {
                throw new QuartzDemoException(QuartzDemoExceptionTypes.JOB_NOT_EXISTS);
            }
            TriggerKey triggerKey = TriggerKey.triggerKey(name, group);
            // 停止触发器
            scheduler.pauseTrigger(triggerKey);
            // 移除触发器
            scheduler.unscheduleJob(triggerKey);
            // 删除任务
            if (!scheduler.deleteJob(JobKey.jobKey(name, group))) {
                log.error("remove failed, name={}, group={}", name, group);
                throw new QuartzDemoException(QuartzDemoExceptionTypes.JOB_DEL_FAILED);
            }
        } catch (SchedulerException e) {
            log.error("remove failed, name={}, group={}", name, group, e);
            throw new QuartzDemoException(QuartzDemoExceptionTypes.JOB_DEL_FAILED);
        }
    }

    private boolean isValidCron(String cron) {
        if (StringUtils.isBlank(cron)) return false;
        String[] cronArr = cron.split(" ");
        if (cronArr.length < 5 || cronArr.length > 6) {
            return false;
        }
        for (String s : cronArr) {
            if (!s.matches(cronRegex)) {
                return false;
            }
        }
        return true;
    }
}
